﻿using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Trippy.Abilities;
using Trippy.Animation;
using Trippy.Camera;
using Trippy.CollidableGameComponents.Agents;
using Trippy.CollidableGameComponents.Events;
using Trippy.CollidableGameComponents.Objects;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
using Trippy.Effects;
using Trippy.Scenery;

namespace Trippy.Levels
{
    class FirstLevel : Level
    {
        public FirstLevel(TrippyGame game, Hero hero)
            : base(game, hero)
        {
            TopPadding = 500f;
            BottomPadding = 500f;
            RightPadding = 500f;
            LeftPadding = 500f;
            Friction = 0.85f;
            UIColor = Color.White;

            if (hero == null)
            {
                Hero = new Hero(this);
            }
            Generate();
            UpdateBounds();
        }

        public override void Initialize()
        {
            Background = Game.Content.Load<Texture2D>("Background/forest");
            MediaPlayer.Play(Game.gameSong);
            MediaPlayer.IsRepeating = true;
            Game.rainLoop.Volume = 0.1f;
            Game.rainLoop.Play();
            base.Initialize();
        }

        protected override void Generate()
        {
            eventManager = new EventManager(this);
            Add(eventManager);

            Random random = new Random();

            var ssign = new SkullSign(this);
            ssign.Position = new Vector2(-400f, 3600f);
            ssign.Scale = new Vector2(.75f);
            Add(ssign);

            Rope r = new Rope(this);
            r.Position = new Vector2(-215, 1200);
            Add(r);

            r = new Rope(this);
            r.Position = new Vector2(215, 1150);
            Add(r);

            WoodBeam beam = new WoodBeam(this);
            beam.Position = new Vector2(0f, 500f);
            beam.Scale = new Vector2(.6f);
            Add(beam);

            for (int i = -5; i < 6; i++)
            {
                Orb o = new Orb(this);
                o.Position = new Vector2(-100f + 50f*i, 420f);
                o.Scale = new Vector2(.3f);
                o.Value = random.Next(1,4);
                Add(o);
            }

            beam = new WoodBeam(this);
            beam.Position = new Vector2(0f, 1500f);
            beam.Scale = new Vector2(.6f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(750f, 650f);
            beam.Scale = new Vector2(.3f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(1000f, 950f);
            beam.Scale = new Vector2(.34f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(700f, 1300f);
            beam.Scale = new Vector2(.26f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(500f, 1150f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(440f, 1400f);
            beam.Scale = new Vector2(.2f);
            Add(beam);

            PowerUp pu = new PowerUp(this, new Shrink(this));
            pu.Position = new Vector2(420f, 1360f);
            Add(pu);

            for (int i = 0; i < 11; i++)
            {
                Orb o = new Orb(this);
                o.Position = new Vector2(550f, 1500f + 50f * i);
                o.Scale = new Vector2(.3f);
                o.Value = random.Next(1, 4);
                Add(o);
            }

            beam = new WoodBeam(this);
            beam.Position = new Vector2(-600f, 2400f);
            beam.Scale = new Vector2(.4f);
            Add(beam);

            for (int j = 0; j < 5; j++)
            {
                beam = new WoodBeam(this);
                beam.Position = new Vector2(1300*j, 3182f);
                beam.Scale = new Vector2(.8f, .6f);
                Add(beam);

                for (int i = -19; i < 20; i++)
                {

                    Spikes spikes = new Spikes(this);
                    spikes.Position = new Vector2(1300 * j + 30 * i, 3125);
                    spikes.Scale = new Vector2(.5f);
                    Add(spikes);

                    if (i % 2 == 0 && j < 3)
                    {
                        Orb o = new Orb(this);
                        o.Position = new Vector2(1300 * j + 30 * i, 3075);
                        o.Value = 1;
                        o.Scale = new Vector2(.3f);
                        Add(o);
                    }
                }
            }

            beam = new WoodBeam(this);
            beam.Position = new Vector2(-750, 2682f);
            beam.Scale = new Vector2(1.6f, .6f);
            Add(beam);

            for (int i = -39; i < 40; i++)
            {

                Spikes spikes = new Spikes(this);
                spikes.Position = new Vector2(-750 + 30 * i, 2625);
                spikes.Scale = new Vector2(.5f);
                Add(spikes);
            }

            beam = new WoodBeam(this);
            beam.Position = new Vector2(2600f, 2400f);
            beam.Scale = new Vector2(.4f);
            Add(beam);

            var movingBlock = new FallingWoodBox(this);
            movingBlock.Position = new Vector2(-600f, 1500f);
            movingBlock.Scale = new Vector2(1.0f, .25f);
            Add(movingBlock);


            BlockWave wave = new BlockWave(this);
            BlockWave wave2 = new BlockWave(this);
            for (int i = 0; i < 28; i++)
            {
                if (i % 4 == 0)
                {
                    Orb o = new Orb(this);
                    o.Position = new Vector2(-250f + 90 * i, 2400f);
                    o.Value = 3;
                    o.Scale = new Vector2(.3f);
                    Add(o);
                    wave2.Add(o);
                }
                else
                {
                    WoodBox woodBox = new WoodBox(this);
                    woodBox.Position = new Vector2(-250f + 90 * i, 2400f);
                    woodBox.Scale = new Vector2(.35f, .35f);
                    Add(woodBox);
                    wave.Add(woodBox);
                }
                
            }

            Add(wave);
            Add(wave2);

            var checkpoint = new Checkpoint(this);
            checkpoint.Position = new Vector2(2600, 2170);
            checkpoint.Scale = new Vector2(.6f);
            Add(checkpoint);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(2600f, 2400f);
            beam.Scale = new Vector2(.4f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(440f, 1400f);
            beam.Scale = new Vector2(.2f);
            Add(beam);

            //Shannon's adds.
            /*
            pu = new PowerUp(this, new Sprint(this));
            pu.Position = new Vector2(2800f, 2350f);
            Add(pu);
             */

            pu = new PowerUp(this, new Fire(this));
            pu.Position = new Vector2(3025f, 2200f);
            Add(pu);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(3325f, 2400f);
            beam.Scale = new Vector2(.2f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(3600f, 2500f);
            beam.Scale = new Vector2(.3f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(4000f, 2500f);
            beam.Scale = new Vector2(.3f);
            Add(beam);

            PathingEnemy pa = new PathingEnemy(this, new Vector2(3800f, 2430f));
            pa.Position = new Vector2(3800f, 2430f);
            pa.Velocity = new Vector2(100f, 0f);
            Add(pa);

            for (int i = 0; i < 5; i++)
            {
                Orb o = new Orb(this);
                o.Position = new Vector2(3950 + 50f * i, 2450);
                o.Scale = new Vector2(.3f);
                o.Value = random.Next(1, 4);
                Add(o);
            }

            pu = new PowerUp(this, new Scry(this));
            pu.Position = new Vector2(4200f, 2450f);
            Add(pu);

            for (int i = 0; i < 10; i++)
            {
                if (i != 3)
                {
                    Orb o = new Orb(this);
                    o.Position = new Vector2(3325f, 2450f + 50f * i);
                    o.Scale = new Vector2(.3f);
                    o.Value = random.Next(1, 4);
                    Add(o);
                }
            }

            pu = new PowerUp(this, new LimitedRockets(this, 1));
            pu.Position = new Vector2(3325f, 2600f);
            Add(pu);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(3325f, 3040f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(3800f, 2880f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            Enemy en2 = new Enemy(this);
            en2.Position = new Vector2(3800f, 2870f);
            Add(en2);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(4000f, 2980f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            pu = new PowerUp(this, new Jump(this));
            pu.Position = new Vector2(4000f, 2930f);
            Add(pu);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(4400f, 2880f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(4800f, 2980f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            en2 = new Enemy(this);
            en2.Position = new Vector2(4800f, 2970f);
            Add(en2);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(5200f, 2880f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(5600f, 2780f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            pu = new PowerUp(this, new Life(this));
            pu.Position = new Vector2(5600f, 2730f);
            Add(pu);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(5800f, 2860f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            pu = new PowerUp(this, new Invincibility(this, 30f));
            pu.Position = new Vector2(5800f, 2810f);
            Add(pu);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(3600f, 2980f);
            beam.Scale = new Vector2(.1f);
            Add(beam);

            Enemy en = new Enemy(this);
            en.Position = new Vector2(3600f, 2970f);
            Add(en);

            pu = new PowerUp(this, new Invincibility(this, 10f));
            pu.Position = new Vector2(3300f, 3000f);
            Add(pu);

            pu = new PowerUp(this, new PassiveInvincibility(this, 10f));
            pu.Position = new Vector2(1950f, 2950f);
            Add(pu);

            pu = new PowerUp(this, new Fly(this));
            pu.Position = new Vector2(650f, 2950f);
            Add(pu);

            pu = new PowerUp(this, new Key(this));
            pu.Position = new Vector2(-1450f, 3150f);
            Add(pu);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(-650f, 3325f);
            beam.Scale = new Vector2(0.2f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(-1050f, 3225f);
            beam.Scale = new Vector2(0.15f);
            Add(beam);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(-1450f, 3325f);
            beam.Scale = new Vector2(0.15f);
            Add(beam);

            SwoopingEnemy se = new SwoopingEnemy(this);
            se.Position = new Vector2(-1650f, 3000f);
            Add(se);

            Door door = new Door(this);
            door.Position = new Vector2(-550f, 3275f);
            Add(door);

            beam = new WoodBeam(this);
            beam.Position = new Vector2(-400f, 3700f);
            beam.Scale = new Vector2(0.3f);
            Add(beam);

            /*
            pu = new PowerUp(this, new Firestorm(this));
            pu.Position = new Vector2(-400f, 3650f);
            Add(pu);
             */

            // create a new event object
            Event e = new Event(this);

            // where you want the event
            e.Position = new Vector2(-600f, 2400f);

            // assign an event to happen upon collision with the event object 
            // (using the AssignHandlers function you can assign different (or no) functions to all of the 4 sides
            e.AssignHandler
                (
                    delegate()
                    {
                        // freeze the hero for the cutscene
                        Hero.Frozen = true;
                        Hero.Velocity = new Vector2();

                        // freeze the event so the cutscene can't happen twice
                        e.Frozen = true;

                        // schedule a zoom out immediately
                        eventManager.Schedule(() => Camera.LinearZoom(.75f, .3f), 0.0);

                        // schedule a camera shift to the checkpoint to happen 1 seconds into the future 
                        eventManager.Schedule(() => Camera.LinearShift(checkpoint, 500f), 0.5f);

                        // schedule a zoom in after a while
                        eventManager.Schedule(() => Camera.LinearZoom(1f, .08f), 3.0);

                        // schedule a camera shift back to the hero (as well as unfreezing the hero) for 8 seconds from now
                        eventManager.Schedule(delegate
                                                  {
                                                      Camera.LinearShift(Hero, 3500f);
                                                      Hero.Frozen = false;
                                                  }, 
                                                  8.0f);
                    }
                );
            // add the event object to the world
            Add(e);

            Event e2 = new Event(this); //checkpoint pan to goal.
            e2.Position = new Vector2(2600f, 2400f);

            // using this to target the camera in e2
            Event e3 = new Event(this);
            e3.Position = new Vector2(-400f, 3700f);

            // assign an event to happen upon collision with the event object 
            // (using the AssignHandlers function you can assign different (or no) functions to all of the 4 sides
            e2.AssignHandler
                (
                    delegate()
                    {
                        // freeze the hero for the cutscene
                        Hero.Frozen = true;
                        Hero.Velocity = new Vector2();

                        // freeze the event so the cutscene can't happen twice
                        e2.Frozen = true;

                        eventManager.Schedule(() => Camera.LinearZoom(.75f, .3f), 0.0f);
                        eventManager.Schedule(() => Camera.LinearShift(pa, 500f), 1f);
                        eventManager.Schedule(() => Camera.LinearShift(en, 500f), 3.5f);
                        eventManager.Schedule(() => Camera.LinearShift(e3, 500f), 5.5f);
                        eventManager.Schedule(() => Camera.LinearZoom(1f, .08f), 12f);

                        eventManager.Schedule(delegate
                                                  {
                                                      Camera.LinearShift(Hero, 3500f);
                                                      Hero.Frozen = false;
                                                  },
                                                  15.5f);
                    }
                );
            // add the event object to the world
            Add(e2);

            /*
            se = new SwoopingEnemy(this);
            se.Position = new Vector2(-300f, 3600f);
            Add(se);

            se = new SwoopingEnemy(this);
            se.Position = new Vector2(-500f, 3800f);
            Add(se);

            se = new SwoopingEnemy(this);
            se.Position = new Vector2(-300f, 3800f);
            Add(se);
            */

            // assign an event to happen upon collision with the event object 
            // (using the AssignHandlers function you can assign different (or no) functions to all of the 4 sides
            e3.AssignHandler
                (
                    delegate()
                    {
                        Hero.Frozen = true;
                        Hero.Velocity = new Vector2();
                        Hero.CurrentAbility = new Invincibility(this, 0.1f);

                        e3.Frozen = true;

                        eventManager.Schedule(() => Camera.LinearZoom(0.001f, 0.25f), 0.0f);
                        /*
                        eventManager.Schedule(delegate
                        {
                            Hero.Position = new Vector2();
                        },
                        4.5f); //hotfix for hero can't pass through to boss level with current position as is
                         */
                        //BUG: cannot currently pass hero through to BossFight1, error with space partitioner out of range.
                        eventManager.Schedule(delegate
                        {
                            var bossLevel = new BossFight1(Game, null); // if no hero is passed as a parameter, one is automatically created
                            Game.Components.Clear();
                            Game.Components.Add(bossLevel);
                            Game.currentState = bossLevel;
                        },
                        5.0f);
                        eventManager.Schedule(() => Camera.InstantZoom(1f), 5.0f);
                    }
                );
            // add the event object to the world
            Add(e3);

            Add(Hero);

            pu = new PowerUp(this, new Fly(this));
            pu.Position = new Vector2(-700f, 2000f);
            Add(pu);

            for (int i = 0; i < 8; i++)
            {
                Orb o = new Orb(this);
                o.Position = new Vector2(-600f, 1600 + i * 100f);
                o.Scale = new Vector2(.3f);
                o.Value = random.Next(1, 4);
                Add(o);
            }

            for (int i = 0; i < 8; i++)
            {
                Orb o = new Orb(this);
                o.Position = new Vector2(-500f, 1600 + i * 100f);
                o.Scale = new Vector2(.3f);
                o.Value = random.Next(1, 4);
                Add(o);
            }

            r = new Rope(this);
            r.Position = new Vector2(-330, 1200);
            Add(r);

            r = new Rope(this);
            r.Position = new Vector2(315, 1200);
            Add(r);

            var sign = new Level1Sign(this);
            sign.Position = new Vector2(315, 700);
            sign.Scale = new Vector2(.55f);
            Add(sign);

            SpawnPoint = new Vector2(0f, 0f);
            Gravity = TrippyConstants.World.Gravity;
            Hero.Acceleration = Gravity;

            Camera = new TrippyCamera(this, Hero);
            Add(Camera);

            Weather w = new Weather(this, 500f, 8f, 20f, 90f, new Vector2(.2f));
            Add(w);
            base.Generate();
        }
    }
}
